﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using FilterData;
using BYServer.SocketEventArgs;
using Common;
using BYServer.Ping;

namespace BYServer
{
    public class YUser
    {
        public Socket sock;
        public IPAddress ip;
        public int port;
        byte[] ThreadBuffer = new byte[1024];
        byte[] ArrayByteReceive;
        byte[] ArrayByteSend;
        public int ID;
        Queue<Action> QueueSendData;       
        FilterDataManager FManager;
        YServer server;        

        System.Timers.Timer CheckDisconnect;
        public YUser(Socket client,YServer server,IPEndPoint endpoint,int IdUser)
        {
            
            this.sock = client;
            this.server = server;
            ip = endpoint.Address;
            port = endpoint.Port;
            ID = IdUser;           
            QueueSendData = new Queue<Action>();
            
            FManager = new FilterDataManager();
            CheckDisconnect = new System.Timers.Timer(60000);
            CheckDisconnect.Elapsed += new System.Timers.ElapsedEventHandler(CheckDisconnect_Elapsed);            
        }        
          
        public void BeginReceive()
        {          
            sock.BeginReceive(ThreadBuffer, 0, 2, SocketFlags.None, new AsyncCallback(OnSizeReceive), sock);
            StartCheckDisconnect();
        }
        int SumSizeReceive = 0;
        int SizeReceived = 0;
        void OnSizeReceive(IAsyncResult ar)
        {
            try
            {
                if (!ar.IsCompleted)
                {
                    Console.WriteLine("Send Size Failed");
                    return;
                }
                sock.EndReceive(ar);
                StopCheckDisconnect();
                byte[] received = new byte[] {ThreadBuffer[0],ThreadBuffer[1] };
                SumSizeReceive = (int)BitConverter.ToInt16(received, 0);
                if (sock.Connected)
                {
                    if (SumSizeReceive > 0)
                    {
                        sock.BeginReceive(ThreadBuffer, 2, SumSizeReceive, SocketFlags.None, new AsyncCallback(OnReceive), sock);
                        StartCheckDisconnect();

                    }
                    else
                    {

                        sock.BeginReceive(ThreadBuffer, 0, 2, SocketFlags.None, new AsyncCallback(OnSizeReceive), sock);
                        StartCheckDisconnect();

                    }
                }
            }
            catch (System.Exception e)
            {
                ClientDisconnected();
            }
        }
        void OnReceive(IAsyncResult ar)
        {
            try
            {
                if (!ar.IsCompleted)
                {
                    Console.WriteLine("Receive Error");
                    return;
                }
                SocketError error;
                int i = sock.EndReceive(ar, out error);
                StopCheckDisconnect();
                if (i < 1)
                {
                    Console.WriteLine("Receive Error");
                    return;
                }
                SizeReceived += i;
                if (sock.Connected)
                {
                    if (SizeReceived < SumSizeReceive)
                    {

                        sock.BeginReceive(ThreadBuffer, SizeReceived + 2, SumSizeReceive - SizeReceived, SocketFlags.None, new AsyncCallback(OnReceive), sock);
                        StartCheckDisconnect();

                    }
                    else
                    {
                        ProcessReceiveFullData();
                        ResetSumReceive();

                        sock.BeginReceive(ThreadBuffer, 0, 2, SocketFlags.None, new AsyncCallback(OnSizeReceive), null);
                        StartCheckDisconnect();

                    }
                }



            }
            catch (System.Exception e)
            {
                ClientDisconnected();
            }
        }
        void ProcessReceiveFullData()
        {
            ArrayByteReceive = FManager.ProcessDataReceive(ThreadBuffer);            
            if (XSocketRecive != null)
                XSocketRecive(sock, new XSocketReciveEventArgs(ArrayByteReceive, this));
        }
        void ResetSumReceive()
        {
            SumSizeReceive = 0;
            SizeReceived = 0;
            ThreadBuffer = new byte[1024];
        }       

        void SendData(byte[] data)
        {
            StopCheckDisconnect();
            ArrayByteSend = FManager.ProcessDataSend(data);
            if (sock.Connected)
            StartCheckDisconnect();            
            sock.BeginSend(ArrayByteSend, 0, ArrayByteSend.Length, SocketFlags.None, new AsyncCallback(OnSend), null);
            
        }       
        void OnSend(IAsyncResult ar)
        {
            try
            {
                if (!ar.IsCompleted || !sock.Connected)
                    Console.WriteLine("Send no complete");
                sock.EndSend(ar);               
                StopCheckDisconnect();
                if(sock.Connected)
                StartCheckDisconnect();                
            }
            catch (System.Exception e)
            {                
                ClientDisconnected();
            }
        }

      
        public void AddQueueDataSend(YMSG message,byte[] data)
        {
            List<byte> tmp = new List<byte>();
            tmp.Add((byte)message);
            tmp.AddRange(data);
            QueueSendData.Enqueue(new Action(() => SendData(tmp.ToArray())));
            ProcessSendData();
        }
        public void AddQueueDataSend(YMSG message, params object[] param)
        {
            List<byte> tmp = new List<byte>();
            foreach (object i in param)
            {
                tmp.Add((byte)(int)i);
            }
            tmp.Insert(0, (byte)message);
            QueueSendData.Enqueue(new Action(() => SendData(tmp.ToArray())));
            ProcessSendData();
        }
       

        public void ProcessSendData()
        {
            if (QueueSendData.Count > 0)
                QueueSendData.Dequeue()();
        }
        
       

       

        int PingMe()
        {
            return PingClient.PingHost(ip,port);
        }

        void StartCheckDisconnect()
        {
            //CheckDisconnect.Start();          
        }
        void StopCheckDisconnect()
        {
            CheckDisconnect.Stop();          
        }
        void CheckDisconnect_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            StopCheckDisconnect();
            int i = PingMe();
            if(i == -1)
            {
                Console.WriteLine("#########   Error Algorithm Ping   ############");
                StartCheckDisconnect();
                return;
            }
            if (i == 4)
            {
                ClientDisconnected();                
                return;
            }
            else
            {
                Console.WriteLine("#########   Ping Complete   ############");
                StartCheckDisconnect();
                Console.WriteLine("########     Continue Check Disconnect     #########");
            }
        }

        void ClientDisconnected()
        {
            sock.Disconnect(false);
            sock.Close();
            sock = null;
            if (XSocketDisconnected != null)
                XSocketDisconnected(this, new XSocketDisconnectEventArgs(this));
        }
        

        public event XSocketReciveHandler XSocketRecive;
        public delegate void XSocketReciveHandler(object sender, XSocketReciveEventArgs args);
        public event XSocketDisconnectedHandler XSocketDisconnected;
        public delegate void XSocketDisconnectedHandler(object sender, XSocketDisconnectEventArgs args);
    }
}
